#include "physicallinkmanager.h"

#include "thirdparty/rapidjson/tools.h"
#include "iot_debug.h"
#include "config.h"
#include "util_tools.h"
#include "string_match.h"
#include "modulemanager.h"

#include <sstream>
#include <logger.h>

using namespace rapidjson;
ConfigSetting g_config;

PhysicalLinkManager::~PhysicalLinkManager()
{
    IPhyLinks::iterator it=phy_devs_.begin();
    while (it!=phy_devs_.end())
    {
        phy_devs_.erase(it);
    }
}

PhysicalLinkManager *PhysicalLinkManager::mgr()
{
    static PhysicalLinkManager linker;
    return &linker;
}

bool PhysicalLinkManager::loadConfig(void)
{
    Document doc;
    const Value *coms = nullptr;
    std::string path, path_compatible;
    path = std::string(RUNTIME_PATH) + std::string(GATHER_CONFIG_FILE);
    path_compatible = std::string(GATHER_CONFIG_FILE);

    g_config.Realdata_Reportperiod     = 300;
    g_config.OfflineState_Reportperiod = 60;
    g_config.OnlineState_Reportperiod  = 60;

    if (!parseFile(path.c_str(), doc))
    {
        if (!parseFile(path_compatible.c_str(), doc))
        {
            return false;
        }
        else{}
    }
    else{}

    coms = getChild(doc, "gather_config");
    if (coms)
    {
        g_config.Realdata_Reportperiod = getInt<uint32_t>(coms, "RealdataReportperiod", 300);
        g_config.OfflineState_Reportperiod = getInt<uint32_t>(coms, "OfflineStateReportperiod", 60);
        g_config.OnlineState_Reportperiod = getInt<uint32_t>(coms, "OnlineStateReportperiod", 60);
    }
    else{}

    return true;
}

bool PhysicalLinkManager::createCom()
{
    //SLOT0_RS485-m
    for (int m = 1; m <= 3; ++m)
    {
        std::string name = std::string("SLOT0_RS485-") + std::to_string(m);
        coms_[name] = name;
    }

    //SLOT0_DI-m
    for (int m = 1; m <= 2; ++m)
    {
        std::string name = std::string("SLOT0_DI-") + std::to_string(m);
        coms_[name] = name;
    }

    //SLOTn_RS485-m
    for (int n = 1; n <= 5; ++n)
    {
        for (int m = 1; m <= 4; ++m)
        {
            std::string name = std::string("SLOT") + std::to_string(n) + std::string("_RS485-") + std::to_string(m);
            coms_[name] = name;
        }
    }

    //SLOTn_RS232-m
    for (int n = 1; n <= 5 ; ++n)
    {
        for (int m = 1; m <= 1; ++m)
        {
            std::string name = std::string("SLOT") + std::to_string(n) + std::string("_RS232-") + std::to_string(m);
            coms_[name] = name;
        }
    }

    //SLOTn_DO-m
    for (int n = 1; n <= 5; ++n)
    {
        for (int m = 1; m <= 3; ++m)
        {
            std::string name = std::string("SLOT") + std::to_string(n) + std::string("_DO-") + std::to_string(m);
            coms_[name] = name;
        }
    }

    //SLOTn_DI-m
    for (int n = 1; n <= 5; ++n)
    {
        for (int m = 1; m <= 5; ++m)
        {
            std::string name = std::string("SLOT") + std::to_string(n) + std::string("_DI-") + std::to_string(m);
            coms_[name] = name;
        }
    }

    //SLOTn_LCC
    for (int n = 1; n <= 2; ++n)
    {
        std::string name = std::string("SLOT") + std::to_string(n) + std::string("_LCC");
        coms_[name] = name;
    }

    return true;
}

std::map<std::string, std::string>& PhysicalLinkManager::getRemapCom(void)
{
    return coms_;
}

bool PhysicalLinkManager::loadCom()
{
    int rc = false;
    bool ret = false;
    Document doc;
    const Value * coms = nullptr;
    int item = 0;
    const char *path = RUNTIME_PATH "comSettings.json";

    if (!parseFile(path, doc))
    {
        FILE * fp = fopen(path, "r");
        if (fp == NULL)
        {
            ErrCode = OPEN_COMSETTING_FAIL;
        }
        else
        {
            fclose(fp);
            ErrCode = PARSE_COMSETTING_FAIL;
        }

        Print_err_log(ErrCode);
        goto exitCode;
    }

    coms = getChild(doc, "ComList");
    if (!coms->IsArray())
    {
        ErrCode = PARSE_COMSETTING_FAIL;
        Print_err_log(ErrCode);
        goto exitCode;
    }
    settings_.reserve(coms->Size());
    phy_devs_.reserve(coms->Size());

    for (auto it=coms->Begin(); it!=coms->End(); it++, item++)
    {
        if (!it->IsObject())
            continue;
        ComSettings *settings = new ComSettings;
        const Value * com = it;
        settings->dump_msg = getInt<bool>(com, "dumpMsg", false);
        settings->uart_name = settings->dev_name = getString(com, "name");
        settings->is_uart = true;

        if (settings->dev_name.empty())
        {
            delete settings;
            ErrCode = PARSE_COMSETTING_FAIL;
            Print_err_log(ErrCode);
            goto exitCode;
        }

        ret = splitTcpDevice(settings->dev_name, settings->tcp.ip, settings->tcp.port);
        if (ret && settings->tcp.port == 36073)
        {
            log_error("SyyGather not support AC!\n");
            ErrCode = PARSE_COMSETTING_FAIL;
            Print_err_log(ErrCode);
            goto exitCode;
        }

        if (ret && settings->tcp.port != 36072)
        {
            settings->is_uart = false;
        }
        else
        {
            if (coms_.find(settings->dev_name) != coms_.end())
                settings->dev_name = coms_[settings->dev_name];

            settings->uart.baudrate = getInt<uint32_t>(com, "baudrate", 9600);
            settings->uart.parity = getString(com, "parity", "N")[0];

            if (settings->uart.parity == 'N')
            {
                settings->uart.paritystr_ = "none";
            }
            if (settings->uart.parity == 'O')
            {
                settings->uart.paritystr_ = "add";
            }
            if (settings->uart.parity == 'E')
            {
                settings->uart.paritystr_ = "even";
            }
            else
            {
                settings->uart.paritystr_ = "none";
            }
        }

        settings->recv_frame_timeout_ms = getInt<uint32_t>(com, "recvFrameTimeoutMs", 3000);
        settings->recv_char_timeout_ms = getInt<uint32_t>(com, "recvCharTimeoutMs", 500);
        settings->retry_cnt_at_recv_timeout = getInt<uint32_t>(com, "retryCntAtRecvTimeout", 3);

        settings->poll_call_wait_time_ms = getInt<uint32_t>(com, "pollCallWaitTimeMs", 100);
        settings->ignore_call_cnt_after_link_lost = getInt<uint32_t>(com, "ignoreCallCntAfterLinkLost", 20);
        settings->cmd_interval_ms = getInt<uint32_t>(com, "cmdIntervalMs", 20);

        {
            const Value *iec101 = nullptr;
            iec101 = getChild(com, "IEC101");

            if(iec101 && iec101->IsObject())
            {
                settings->iec101.cot_len = getInt<uint16_t>(iec101, "cotLen", 1);
                settings->iec101.coa_len = getInt<uint16_t>(iec101, "coaLen", 1);
                settings->iec101.info_obj_addr_len = getInt<uint16_t>(iec101, "infoObjectAddrLen", 2);
                settings->iec101.link_addr_len = getInt<uint16_t>(iec101, "linkAddrLen", 1);
                settings->iec101.gen_again_time_ms = getInt<uint32_t>(iec101, "genTimeMs", 900000);
                settings->iec101.ignore_quality = getInt<bool>(iec101, "ignoreQuality", true);
                settings->iec101.reversal_fcb_after_link_lost = getInt<bool>(iec101, "reversalFCBAfterLinkLost", false);
            }
            else
            {
                settings->iec101.cot_len = 1;
                settings->iec101.coa_len = 1;
                settings->iec101.info_obj_addr_len = 2;
                settings->iec101.link_addr_len = 1;
                settings->iec101.gen_again_time_ms = 900000;
                settings->iec101.ignore_quality = true;
                settings->iec101.reversal_fcb_after_link_lost = false;

            }
        }

        settings_.push_back(settings);
        phy_devs_.push_back(nullptr);
    }

    rc = true;
exitCode:
    return rc;
}

uint32_t PhysicalLinkManager::devNum() const
{
    return phy_devs_.size();
}

IPhyLink *PhysicalLinkManager::getDevice(uint32_t index, bool create)
{
    if (index < phy_devs_.size())
    {
        IPhyLink *dev = phy_devs_[index];
        if (create && dev == nullptr)
        {
            dev = IPhyLink::createDev(settings_[index]);
            phy_devs_[index] = dev;
        }

        return dev;
    }
    return nullptr;
}

int PhysicalLinkManager::devIndex(const char *dev_name) const
{
    for (uint32_t i=0; i<settings_.size(); i++)
    {
        if (settings_[i]->dev_name.compare(dev_name) == 0)
        {
            return static_cast<int>(i);
        }
    }

    return -1;
}

int PhysicalLinkManager::portIndex(const char *dev_name) const
{
    for (uint32_t i=0; i<settings_.size(); i++)
    {
        if (settings_[i]->uart_name.compare(dev_name) == 0)
        {
            return static_cast<int>(i);
        }
    }

    return -1;
}

const char *PhysicalLinkManager::devName(uint32_t index) const
{
    if ( index < settings_.size())
        return settings_[index]->dev_name.c_str();

    return nullptr;
}

bool PhysicalLinkManager::setDumpFlag(uint32_t index, bool enable)
{
    if (index < settings_.size())
    {
        settings_[index]->dump_msg = enable;
        return true;
    }

    return false;
}

bool PhysicalLinkManager::enableDump(uint32_t index) const
{
    if (index < settings_.size())
    {
        return settings_[index]->dump_msg;
    }

    return false;
}

const char * PhysicalLinkManager::getPortName(const char *dev_name) const
{
    for (auto com=coms_.begin(); com!=coms_.end(); com++)
    {
        if (com->second.compare(dev_name) == 0)
            return com->first.c_str();
    }

    return dev_name;
}

const char *PhysicalLinkManager::getDeviceName(const char *port) const
{
    for (auto com=coms_.begin(); com!=coms_.end(); com++)
    {
        if (com->first.compare(port) == 0)
            return com->second.c_str();
    }

    return port;
}

PhysicalLinkManager::PhysicalLinkManager()
{

}

void PhysicalLinkManager::updateRemapCom()
{
    for (uint32_t i = 0; i < settings_.size(); ++i)
    {
        if (coms_.find(settings_[i]->uart_name) != coms_.end())
        {
            settings_[i]->dev_name = coms_[settings_[i]->uart_name];
        }
        IPhyLink* link = getDevice(i, false);
        if (link)
        {
            link->SwitchSettings(settings_[i], false);
        }
    }
}